import { message } from 'antd';
import { parse } from 'qs';
import pathRegexp from 'path-to-regexp';
import moment from 'moment';
import { Route } from '@/models/connect';
import { JSEncrypt } from 'jsencrypt';
import md5 from 'js-md5';
import ScratchBlocks from '@scene/scene-blocks';
import { isObject, isString, isArray, cloneDeep } from 'lodash';

import { editGlobalState, umiDispatch } from '@/layouts/BasicLayout';
import { SceneData } from './block';
import log from '@/lib/log';
import * as XLSX from 'xlsx';
import { fingerprintFetch } from './request';

/* eslint no-useless-escape:0 import/prefer-default-export:0 */
const reg =
  /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;

export const isUrl = (path: string): boolean => reg.test(path);

export const getPageQuery = () => parse(window.location.href.split('?')[1]);

/**
 * props.route.routes
 * @param router [{}]
 * @param pathname string
 */
export const getAuthorityFromRouter = <T extends { path: string }>(
  router: T[] = [],
  pathname: string,
): T | undefined => {
  const authority = router.find(({ path }) => path && pathRegexp(path).exec(pathname));
  if (authority) return authority;
  return undefined;
};

export const getRouteAuthority = (path: string, routeData: Route[]) => {
  let authorities: string[] | string | undefined;
  routeData.forEach(route => {
    // match prefix
    if (pathRegexp(`${route.path}/(.*)`).test(`${path}/`)) {
      if (route.authority) {
        authorities = route.authority;
      }
      // exact match
      if (route.path === path) {
        authorities = route.authority || authorities;
      }
      // get children authority recursively
      if (route.routes) {
        authorities = getRouteAuthority(path, route.routes) || authorities;
      }
    }
  });
  return authorities;
};

/**==========================================================================*/
/**
 * 是否已登录
 */
export function isLogin() {
  // sessionstorage的token存在
  const sessionToken = sessionStorage.getItem('token');
  if (sessionToken) {
    return true;
  }

  // localstorage的token没有过期
  const localTokenExpire = localStorage.getItem('expire');
  if (localTokenExpire) {
    if (moment().isBefore(moment(localTokenExpire), 'day')) {
      return true;
    }
  }
  return false;
}

/**
 * 将token放在本地存储中
 * 如果用户勾选了记住密码，需要将token存入localstorage (有效期14天)
 * @param token
 */
export function putToken(token: string) {
  sessionStorage.setItem('token', token);
  if (localStorage.getItem('remember')) {
    localStorage.setItem('token', token);
    const expire = moment().add(6, 'days').format('YYYY-MM-DD');
    localStorage.setItem('expire', expire);
  }
}

/**
 * 获取token
 * 1. 先从sessionStorage取
 * 2. 再从localStorage取
 */
export function getToken() {
  const sessionToken = sessionStorage.getItem('token');
  if (sessionToken) {
    return sessionToken;
  }

  // 如果当前时间超过localstorage存储的token失效时间，直接清除
  // 如果档期时间没有超过localstorage存储的token失效时间，直接返回token
  const localTokenExpire = localStorage.getItem('expire');
  if (localTokenExpire) {
    if (moment().isBefore(moment(localTokenExpire), 'day')) {
      return localStorage.getItem('token');
    } else {
      localStorage.clear();
    }
  }
  return null;
}

/**
 * 清除所有的本地缓存
 */
export function storageClear() {
  sessionStorage.clear();
  // localStorage.clear();
  localClear();
}

export function sessionClear() {
  sessionStorage.clear();
  // localStorage.clear();
  localClear();
}
//除金融授信车缓存外，清除本地所有缓存
export function localClear() {
  for (const key in localStorage) {
    console.log('key', key);
    if (key !== 'creditData') {
      localStorage.removeItem(key);
      // delete localStorage[key]
    }
  }
}
export function formatJson(json, options) {
  let reg,
    formatted = '',
    pad = 0,
    PADDING = '    ';
  options = options || {};
  options.newlineAfterColonIfBeforeBraceOrBracket =
    options.newlineAfterColonIfBeforeBraceOrBracket === true;
  options.spaceAfterColon = options.spaceAfterColon !== false;
  try {
    if (typeof json !== 'string') {
      json = JSON.stringify(json);
    } else {
      json = JSON.parse(json);
      json = JSON.stringify(json);
    }
  } catch (e) {
    return json;
  }
  reg = /([\{\}])/g;
  json = json.replace(reg, '\r\n$1\r\n');
  reg = /([\[\]])/g;
  json = json.replace(reg, '\r\n$1\r\n');
  reg = /(\,)/g;
  json = json.replace(reg, '$1\r\n');
  reg = /(\r\n\r\n)/g;
  json = json.replace(reg, '\r\n');
  reg = /\r\n\,/g;
  json = json.replace(reg, ',');
  if (!options.newlineAfterColonIfBeforeBraceOrBracket) {
    reg = /\:\r\n\{/g;
    json = json.replace(reg, ':{');
    reg = /\:\r\n\[/g;
    json = json.replace(reg, ':[');
  }
  if (options.spaceAfterColon) {
    reg = /\:/g;
    json = json.replace(reg, ':');
  }
  json.split('\r\n').forEach((node, index) => {
    let i = 0,
      indent = 0,
      padding = '';

    if (node.match(/\{$/) || node.match(/\[$/)) {
      indent = 1;
    } else if (node.match(/\}/) || node.match(/\]/)) {
      if (pad !== 0) {
        pad -= 1;
      }
    } else {
      indent = 0;
    }

    for (i = 0; i < pad; i++) {
      padding += PADDING;
    }

    formatted += `${padding + node}\r\n`;
    pad += indent;
  });
  return formatted;
}

export function setFullscreen(element) {
  const el = element instanceof HTMLElement ? element : document.documentElement;
  if (window.ActiveXObject) {
    let WsShell = new ActiveXObject('WScript.Shell');
    WsShell.SendKeys('{F11}');
  }
  //HTML W3C 提议
  else if (el.requestFullScreen) {
    el.requestFullScreen();
  }
  //IE11
  else if (el.msRequestFullscreen) {
    el.msRequestFullscreen();
  }
  // Webkit (works in Safari5.1 and Chrome 15)
  else if (el.webkitRequestFullScreen) {
    el.webkitRequestFullScreen();
  }
  // Firefox (works in nightly)
  else if (el.mozRequestFullScreen) {
    el.mozRequestFullScreen();
  }
}

export function exitFullscreen() {
  const efs =
    document.exitFullscreen ||
    document.webkitExitFullscreen ||
    document.mozCancelFullScreen ||
    document.msExitFullscreen;
  if (efs) {
    efs.call(document);
  } else if (window.ActiveXObject) {
    const ws = new ActiveXObject('WScript.Shell');
    ws && ws.SendKeys('{F11}');
  }
}

export function encryptedData(data: any, key: any) {
  let encryptor = new JSEncrypt();
  encryptor.setPublicKey(key);
  return encryptor.encrypt(data);
}

export function decryptedData(data: any, key: any) {
  let encryptor = new JSEncrypt();
  encryptor.setPrivateKey(key);
  return encryptor.decrypt(data);
}
export const downloadFileByUrl = (url: string, name: string) => {
  const str = `${window.g.url}/common/getfile/byurl`;
  const obj = { url };
  const dataStr = getKeyValueString(obj);
  const md5 = getMd5(dataStr);

  const options = {
    method: 'POST',
    body: dataStr,
    headers: {
      Accept: 'application/json',
      'Content-Type': 'application/json; charset=utf-8',
      OptAuthorization: getToken() ?? '',
      shaSign: md5,
    },
  };

  fingerprintFetch(str, options)
    .then(response => {
      if (response.status === 200) {
        return response.blob();
      } else {
        throw response;
      }
    })
    .then(blob => {
      const url = window.URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = name;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      window.URL.revokeObjectURL(url);
    })
    .catch(() => {
      message.error('下载失败，请稍后在试!');
    });
};

export function downloadUrl(url: string) {
  const date = new Date();
  const a = document.createElement('a');
  a.setAttribute('href', url);
  a.setAttribute('download', `${date.getTime()}`);
  a.setAttribute('target', '_blank');
  const clickEvent = document.createEvent('MouseEvents');
  clickEvent.initEvent('click', true, true); //模拟点击

  console.log(a);
  a.dispatchEvent(clickEvent);
}

export const unAuthentication = 'no' === 'no';

// 设置全局css变量
export const setProperty = (key: string, value: string) => {
  document.documentElement.style.setProperty(key, value);
};

// 替换url参数
export const replaceUrlParams = (params: any) => {
  if (isObject(params)) {
    const [path, queryString] = window.location.hash.split('?');
    const queryParams = new URLSearchParams(queryString);
    const oldParams = Object.fromEntries(queryParams.entries());
    const newParams = { ...oldParams, ...params };
    const urlParamsList: string[] = [];
    Object.keys(newParams).forEach(key => {
      const target = newParams[key];
      if (isString(target)) {
        urlParamsList.push(`${key}=${target}`);
      }

      if (isArray(target)) {
        urlParamsList.push(`${key}=${target[target.length - 1]}`);
      }
    });

    window.history.replaceState(null, '', `${path}?${urlParamsList.join('&')}`);
  } else {
    log.warn('url params must be object');
  }
};

// replaceUrlParams({ id: '123' });

// 过滤数据
export const filterProjectJson = (data: any): any => {
  const blocks = cloneDeep(data);

  const deleteBlock = (blockId: string): void => {
    const block = blocks[blockId];

    if (!block) {
      return;
    }

    if (block.next !== null) {
      deleteBlock(block.next);
    }

    for (const input in block.inputs) {
      if (block.inputs[input].block !== null) {
        deleteBlock(block.inputs[input].block);
      }

      if (
        block.inputs[input].shadow !== null &&
        block.inputs[input].shadow !== block.inputs[input].block
      ) {
        deleteBlock(block.inputs[input].shadow);
      }
    }

    delete blocks[blockId];
  };

  Object.keys(blocks).forEach(blockId => {
    const targetBlock = blocks[blockId];
    if (targetBlock?.topLevel) {
      if (targetBlock.opcode !== 'control_when') {
        deleteBlock(blockId);
      }
    }
  });

  return blocks;
};

// 异常数据查找函数
export const illegalDataSearch = (data: any, specifyOpcode: string = 'control_when'): string[] => {
  const blocks = cloneDeep(data);

  const illegalData: string[] = [];

  Object.keys(blocks).forEach(blockId => {
    const targetBlock = blocks[blockId];
    if (targetBlock?.topLevel) {
      if (targetBlock.opcode !== specifyOpcode) {
        illegalData.push(blockId);
      }
    }
  });

  return [...new Set(illegalData)];
};

// 替换字段
export const replaceKeys = (data: any, reKeys = {}, ignore: string[] = []): any => {
  const newData = cloneDeep(data);
  if (isArray(newData)) {
    return newData.map(item => replaceKeys(item, reKeys, ignore));
  } else if (isObject(newData)) {
    const result = {};
    Object.keys(newData).forEach(key => {
      if (ignore.indexOf(key) === -1) {
        let newKey = reKeys[key] || key;
        newKey = newKey[0].toUpperCase() + newKey.substr(1);
        const target = newData[key];
        if (isArray(target)) {
          result[`m${newKey}`] = replaceKeys(target, reKeys, ignore);
        } else if (isObject(target)) {
          result[`m${newKey}`] = replaceKeys(target, reKeys, ignore);
        } else {
          result[`m${newKey}`] = target;
        }
      }
    });
    return result;
  }
};

// 定义规则
const inputsMap = {
  control_pre_conditions: {
    // inputs: ['BOOLEAN'],
    // BOOLEAN_MSG: '当, 请删除或补足条件',
    next: true,
    NEXT_MSG: '场景至少有一个动作, 请补足动作',
  },
  control_if: {
    inputs: ['CONDITION'],
    CONDITION_MSG: '如果执行条件为空, 请删除或补足条件',
  },
  control_if_else: {
    inputs: ['CONDITION'],
    CONDITION_MSG: '如果执行否则条件为空, 请删除或补足条件',
  },
  control_exit: {
    inputs: ['CONDITION'],
    CONDITION_MSG: '退出条件为空, 请删除或补足条件',
  },
  operator_and: {
    inputs: ['OPERAND1', 'OPERAND2'],
    OPERAND1_MSG: '与条件左侧为空, 请删除或补足条件',
    OPERAND2_MSG: '与条件右侧为空, 请删除或补足条件',
  },
  operator_or: {
    inputs: ['OPERAND1', 'OPERAND2'],
    OPERAND1_MSG: '或条件左侧为空, 请删除或补足条件',
    OPERAND2_MSG: '或条件右侧为空, 请删除或补足条件',
  },
};

// 验证积木是否空置数据
const checkBlockUnempty = (block: any, rule: any): { msg: string; isFail: boolean } | boolean => {
  if (!rule) {
    return false;
  }

  let msg = '';
  let isFail = false;

  const ruleInputs = rule.inputs;
  const blockInputs = block.inputs;

  // 检查积木状态
  if (ruleInputs) {
    if (ruleInputs.length) {
      for (let index = 0; index < ruleInputs.length; index++) {
        const key = ruleInputs[index];

        if (!blockInputs[key] || !blockInputs[key][1]) {
          isFail = true;
          msg = rule[`${key}_MSG`];
          break;
        }
      }
    }
  }

  if (rule.next) {
    // 检查有没有next积木，没有就异常
    if (!block?.next) {
      isFail = true;
      msg = rule.NEXT_MSG;
    }
  }

  return {
    msg: msg,
    isFail: isFail,
  };
};

// 验证数据是否异常
export const checkProjectJson = (data: any, actionIds: number[]) => {
  const projectJson = cloneDeep(data);

  const errorList = [];

  let isFail = true;
  let msg = '场景至少有一个动作，请补足动作';

  for (const key in projectJson) {
    const block = projectJson[key];

    // 验证数据
    const res = checkBlockUnempty(block, inputsMap[block.opcode]);
    if (res && res?.isFail) {
      errorList.push(res);
    }

    const opcodes = block.opcode.split('_');

    if (actionIds.includes(Number(opcodes[opcodes.length - 1]))) {
      isFail = false;
    }
  }

  if (isFail) {
    errorList.push({
      isFail,
      msg,
    });
  }

  return errorList;
};

// 获取dispatch func
export let getDispatch = () => {
  return umiDispatch;
};

export const setUnFoucs = () => {
  document.querySelector('.blocklyWidgetDiv').style = 'display: none;';
};

export const setBlockValue = (e: any, value: any) => {
  setUnFoucs();
  e.block.setText(value);
  const blocklyHtmlInput = document.querySelector('.blocklyHtmlInput');
  if (blocklyHtmlInput) {
    blocklyHtmlInput.value = value;
  }
};

// 传入opcode 输出实际的能力id 遇到内置输出 空字符串
export function getSkillId(opcode: string): string {
  if (!isString(opcode)) {
    return '';
  }
  const lastIndex = opcode.lastIndexOf('_');
  if (lastIndex === -1) {
    return '';
  }

  const skillId = opcode.substring(lastIndex + 1);

  if (isNaN(Number(skillId))) {
    return '';
  }

  return skillId;
}

// 验证场景数据
export const checkSenceJson = (blocks: SceneData, ignores: number[] = []): number | null => {
  let errStatus = null;

  const Blocks = ScratchBlocks.Blocks;

  for (const key in blocks) {
    const block = blocks[key];
    const skillId = getSkillId(block.opcode);

    // 跳出这个循环 解决占位符不匹配导致的场景解析失败问题
    if (ignores.includes(Number(skillId))) {
      continue;
    }

    if (!Blocks[block.opcode]) {
      const opcodes = block.opcode.split('_');
      errStatus = Number(opcodes[opcodes.length - 1]);
      // errStatus = true;
      console.log('� ~ file: utils.ts ~ line 550 ~ checkSenceJson ~ 缺失能力 id ', errStatus);
      break;
    }
  }

  return errStatus;
};

const findSkillJson = (mID: string | number) => {
  // 根据 mID 在 skillJson 中查找对应的能力信息
  const { skillJson } = editGlobalState;
  const currentSkill = skillJson.find((item: any) => item.mID == mID);
  if (currentSkill) {
    return currentSkill.mName || currentSkill.mDesc;
  } else if (mID == 61699) {
    // 特殊处理，ID为 61699 时返回 '延迟'
    return '延迟';
  } else {
    console.error('能力列表不存在该 id');
    return `不存在该能力 ${mID}`;
  }
};

// 竞合关系判断
export const checkCompetingRelationship = (data: any) => {
  console.time('checkCompetingRelationship');
  // 读取竞合关系规则
  const { editerRules: competingRelationshipRules } = editGlobalState as any;

  // 复制场景json数据
  const blocks = cloneDeep(data);

  // 收集相同积木出现的次数
  const blocksMap = {};
  // 记录可能冲突的次数
  const warningMap: { [key: string]: boolean } = {};

  // 收集相同积木出现的次数
  for (const key in blocks) {
    // 获取当前积木
    const block = blocks[key];

    // 分割 opcode
    const splitArr = block.opcode.split('_');

    // 判断是否为能力积木
    if (splitArr.length === 2) {
      const [, skillId] = splitArr;
      blocksMap[skillId] = (blocksMap[skillId] || 0) + 1;
    } else {
      // 能力积木里的参数积木不需要收集
    }
  }

  // 循环积木 map
  for (const key in blocksMap) {
    // 当前积木的数量
    const blockCounts = blocksMap[key];

    // 读取当前积木的规则信息
    const rules = competingRelationshipRules[key];
    // console.log("� ~ file: utils.ts ~ line 633 ~ checkCompetingRelationship ~ rules", rules)
    // 存在规则
    if (rules) {
      rules.forEach((ruleItem: number) => {
        // 判断是否与自己冲突
        if (ruleItem === Number(key)) {
          if (blockCounts >= 2) {
            // 记录状态
            warningMap[`${key}|${key}`] = true;
          } else {
            // 不记录状态
          }
        } else {
          // 与其他积木出现冲突
          if (blocksMap[ruleItem]) {
            // 判断是否出现
            const [min, max] = [Number(key), ruleItem].sort();
            // 记录状态
            warningMap[`${min}|${max}`] = true;
          }
        }
      });
    }
  }

  // console.log("� ~ file: utils.ts ~ line 627 ~ checkCompetingRelationship ~ warningMap", warningMap)
  // 处理警告数据
  const warningList: string[] = Object.keys(warningMap).map(warningKey => {
    // 生成警告消息
    const [key1, key2] = warningKey.split('|');
    return `${findSkillJson(key1)} 和 ${findSkillJson(key2)} 存在竞合关系`;
  });

  console.timeEnd('checkCompetingRelationship');
  return {
    competingRelationshipStatus: warningList.length > 0,
    warningList,
  };
};

/**
 * 下载流文件
 * @param data 数据
 * @param fileName 文件名称
 * @param type 导出文件类型
 */

export const downloadArrayBufferFile = (
  data: ArrayBuffer,
  fileName: string,
  type: string = 'excel',
) => {
  downloadBlobFile(
    data,
    fileName,
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  );
};
export const downloadBlobFile = (
  data: Buffer | ArrayBuffer | Blob,
  fileName: string,
  type: string = 'excel',
  charset: string = 'charset=UTF-8',
) => {
  try {
    window.URL = window.URL || window.webkitURL;
    // new Blob(['\ufeff', data], ...
    const href = URL.createObjectURL(
      new Blob([data], { type: (blobType[type] || type) + ';' + charset }),
    );
    downloadURLFile(href, fileName);
  } catch (error) {
    console.log(data, fileName, type);
    console.log('导出数据异常', error);
  }
};
export const downloadURLFile = (href: string, fileName: string) => {
  // 创建 href 超链接，点击进行下载
  window.URL = window.URL || window.webkitURL;
  const downA = document.createElement('a');
  downA.href = href;
  downA.download = fileName;
  downA.click();
  // 销毁超连接
  window.URL.revokeObjectURL(href);
};
export const blobType = {
  excel: 'application/vnd.ms-excel', // 下载 Excel
  word: 'application/msword', // 下载 Word
  zip: 'application/zip', // 下载 Zip
  html: 'text/html', // 下载 Html
  markdown: 'text/markdown', // 下载 Markdown
};

// 时间格式转化
export const timeFormatConversion = (timeString: string): string => {
  const timeList = timeString.split(' ');

  if (timeList.length === 1) {
    timeList[0] = timeList[0].replace(/h/g, 'H');
  } else if (timeList.length === 2) {
    timeList[0] = timeList[0].toLocaleUpperCase();
    timeList[1] = timeList[1].replace(/h/g, 'H');
  }

  return timeList.join(' ');
};

// 比较大小
export function compareVals(a: number, b: number): number {
  if (a < b) {
    return -1;
  } else if (a > b) {
    return 1;
  } else {
    return 0;
  }
}

//  获取变量类型
function getDataType(data: any) {
  return Object.prototype.toString.call(data).replace('[object ', '').replace(']', '');
}

//  通过url，options 生成 md5加密
export function encryptSign(url: any, options: any) {
  const { params, data } = options;
  const searchObj = getParamsObj(url);
  const paramsData = Object.assign({}, searchObj, params);
  let paramsStr = '';
  for (let k in paramsData) {
    // let v = params[k] !== null && params[k] !== undefined ? params[k] : ''
    if (paramsData[k] !== undefined) {
      let tempV = paramsData[k] === null ? '' : paramsData[k];
      paramsStr += '&' + k + '=' + tempV;
    }
  }
  paramsStr = paramsStr.slice(1);
  let dataStr = '';
  // let dataStr = JSON.stringify(data)
  let dataType = getDataType(data);
  if (dataType === 'Array') {
    dataStr = JSON.stringify(data);
  } else if (dataType === 'FormData') {
    dataStr = getFormDataString(data);
  } else {
    dataStr = getKeyValueString(data);
  }

  if (dataStr && dataStr.length > 0) {
    let connectStr = paramsStr.length > 0 ? '&' : '';
    paramsStr += connectStr + dataStr;
  }
  return paramsStr.length > 0 ? md5(paramsStr) : '';
}

//  获取params参数对象
export function getParamsObj(url: string) {
  const eleUrl = document.createElement('a');
  eleUrl.href = url;
  const strSearch = eleUrl.search.slice(1);
  const searchObj = {};
  if (strSearch.length > 0) {
    strSearch.split('&').forEach(item => {
      let temp = item.split('=');
      searchObj[temp[0]] = decodeURIComponent(temp[1]);
    });
  }
  return searchObj;
}

//  {key:value} 转 string
export function getKeyValueString(data: any) {
  let dataStr = '';
  if (data) {
    let tempObj = {};
    Object.keys(data).forEach(item => {
      if (data[item] !== undefined) {
        tempObj[item] = data[item];
      }
    });
    dataStr = JSON.stringify(tempObj);
  }
  return dataStr;
}

//  针对 formData 类型数据转 string（用于生成签名）
export function getFormDataString(data: any) {
  let dataStr = '';
  for (let key of data.keys()) {
    let temp = data.get(key);
    let dataType = getDataType(temp);
    if (dataType === 'String') {
      if (temp !== undefined) {
        let tempV = temp === null ? '' : temp;
        dataStr += '&' + key + '=' + tempV;
      }
    }
  }
  return dataStr.slice(1);
}

//  string生成md5
export function getMd5(str: string) {
  return md5(str);
}

// 导出表格
export const exportExlsx = ({ table = [], name = '默认表格' }) => {
  // 创建工作簿
  var ws = XLSX.utils.aoa_to_sheet(table);

  // 创建工作簿对象
  var wb = XLSX.utils.book_new();
  XLSX.utils.book_append_sheet(wb, ws, 'Sheet1');

  // 生成.xlsx文件
  const fileName = `${name}.xlsx`;
  const wbout = XLSX.write(wb, { bookType: 'xlsx', type: 'array' });

  // 创建Blob对象
  const blob = new Blob([wbout], { type: 'application/octet-stream' });
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.setAttribute('download', fileName);
  document.body.appendChild(link);

  link.click();
  URL.revokeObjectURL(link.href);
  link.remove();

};